/* kernel.c - the C part of the kernel */
/* Copyright (C) 1999  Free Software Foundation, Inc.

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */


/* INCLUDES */
#include <multiboot.h>
#include <util.c>


/* CONSTANTS */
/* Some screen stuff. */
/* The number of columns. */
#define COLUMNS                 80
/* The number of lines. */
#define LINES                   24
/* The attribute of an character. */
#define ATTRIBUTE               7
/* The video memory address. */
#define VIDEO                   0xB8000
#define BITMAP_LENGTH			5
/* The memory block size in bytes */
#define MEMORY_BLOCK_SIZE		1024

//Define para utilizada no SpeedCPU
#define PORT 0x3f8   /* COM1 */

#define RTCctrl 0x70
#define RTCdata 0x71

#define RTCsec  0x00
//#define RTCmin  0x02
//#define RTChr   0x04

//Define para interrupções
#define PIC1 0x20
#define PIC2 0xA0

#define ICW1 0x11
#define ICW4 0x01




/* VARIABLES */
/* Save the X position. */
static int xpos;
/* Save the Y position. */
static int ypos;
/* Point to the video memory. */
static volatile unsigned char *video;
/* Bitmap structure for memory manager */
static byte_t bitmap[BITMAP_LENGTH];
static int* pointerMalloc = 0;


/* FORWARD DECLARATIONS */
void cmain(unsigned long magic, unsigned long addr);
static void cls(void);
static void itoa(char *buf, int base, int d);
static void putchar(int c);
static int ceil(double value);
void printf(const char *format, ...);
void* malloc(size_t size);
void free(void *ptr, size_t size);
void printBitmap();

static __inline__ unsigned long long rdtsc(void);
static inline void outb(unsigned short port, unsigned char val);
static inline unsigned char inb(unsigned short port);
static __inline__ void disable();
unsigned long speedCPU();
void init_pics(int pic1, int pic2);


char* proc_nome();


/* FUNCTIONS */
/* Check if MAGIC is valid and print the Multiboot information structure
 pointed by ADDR. */
void cmain(unsigned long magic, unsigned long addr) {
	multiboot_info_t *mbi;

	/* Clear the screen. */
	cls();

	/* Am I booted by a Multiboot-compliant boot loader? */
	if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
	{
		printf("Invalid magic number: 0x%x\n", (unsigned) magic);
		return;
	}

	/* Set MBI to the address of the Multiboot information structure. */
	mbi = (multiboot_info_t *) addr;

	/* Print out the flags. */
	printf("flags = 0x%x\n", (unsigned) mbi->flags);

	/* Are mem_* valid? */
	if (CHECK_FLAG (mbi->flags, 0))
		printf("mem_lower = %uKB, mem_upper = %uKB\n",
				(unsigned) mbi->mem_lower, (unsigned) mbi->mem_upper);

	/* Is boot_device valid? */
	if (CHECK_FLAG (mbi->flags, 1))
		printf("boot_device = 0x%x\n", (unsigned) mbi->boot_device);

	/* Is the command line passed? */
	if (CHECK_FLAG (mbi->flags, 2))
		printf("cmdline = %s\n", (char *) mbi->cmdline);

	/* Are mods_* valid? */
	if (CHECK_FLAG (mbi->flags, 3)) {
		multiboot_module_t *mod;
		int i;

		printf("mods_count = %d, mods_addr = 0x%x\n", (int) mbi->mods_count,
				(int) mbi->mods_addr);
		for (i = 0, mod = (multiboot_module_t *) mbi->mods_addr;
				i < mbi->mods_count; i++, mod++)
			printf(" mod_start = 0x%x, mod_end = 0x%x, cmdline = %s\n",
					(unsigned) mod->mod_start, (unsigned) mod->mod_end,
					(char *) mod->cmdline);
	}

	/* Bits 4 and 5 are mutually exclusive! */
	if (CHECK_FLAG (mbi->flags, 4) && CHECK_FLAG (mbi->flags, 5))
	{
		printf("Both bits 4 and 5 are set.\n");
		return;
	}

	/* Is the symbol table of a.out valid? */
	if (CHECK_FLAG (mbi->flags, 4)) {
		multiboot_aout_symbol_table_t *multiboot_aout_sym = &(mbi->u.aout_sym);

		printf("multiboot_aout_symbol_table: tabsize = 0x%0x, "
				"strsize = 0x%x, addr = 0x%x\n",
				(unsigned) multiboot_aout_sym->tabsize,
				(unsigned) multiboot_aout_sym->strsize,
				(unsigned) multiboot_aout_sym->addr);
	}

	/* Is the section header table of ELF valid? */
	if (CHECK_FLAG (mbi->flags, 5)) {
		multiboot_elf_section_header_table_t *multiboot_elf_sec =
				&(mbi->u.elf_sec);

		printf("multiboot_elf_sec: num = %u, size = 0x%x,"
				" addr = 0x%x, shndx = 0x%x\n",
				(unsigned) multiboot_elf_sec->num,
				(unsigned) multiboot_elf_sec->size,
				(unsigned) multiboot_elf_sec->addr,
				(unsigned) multiboot_elf_sec->shndx);
	}

	/* Are mmap_* valid? */
	if (CHECK_FLAG (mbi->flags, 6)) {
		multiboot_memory_map_t *mmap;

		printf("mmap_addr = 0x%x, mmap_length = 0x%x\n",
				(unsigned) mbi->mmap_addr, (unsigned) mbi->mmap_length);
		for (mmap = (multiboot_memory_map_t *) mbi->mmap_addr;
				(unsigned long) mmap < mbi->mmap_addr + mbi->mmap_length;
				mmap = (multiboot_memory_map_t *) ((unsigned long) mmap
						+ mmap->size + sizeof(mmap->size)))
			printf(" size = 0x%x, base_addr = 0x%x%x,"
					" length = 0x%x%x, type = 0x%x\n", (unsigned) mmap->size,
					mmap->addr >> 32, mmap->addr & 0xffffffff, mmap->len >> 32,
					mmap->len & 0xffffffff, (unsigned) mmap->type);
	}

	cls();

	int pMalloc1 = malloc(1024);
	printf("malloc1 = %d", pMalloc1);
	printBitmap();

	int pMalloc2 = malloc(1024);
	printf("malloc2 = %d", pMalloc2);
	printBitmap();

	int pMalloc3 = malloc(1024);
	printf("malloc3 = %d", pMalloc3);
	printBitmap();

	free(pMalloc2, 1024);
	printf("free1 do ponteiro %d, de tamanho %d", pMalloc2, 1024);
	printBitmap();

	int pMalloc4 = malloc(1024);
	printf("malloc4 = %d", pMalloc4);
	printBitmap();

	int pMalloc5 = malloc(1024);
	printf("malloc5 = %d", pMalloc5);
	printBitmap();

	cls();

	printf("SpeedCPU = %d",speedCPU());
	printf("GHz\n");
	printf(proc_nome());

//	printf(init_pics(0x20, 0x28));
}

/* Clear the screen and initialize VIDEO, XPOS and YPOS. */
static void cls(void) {
	int i;

	video = (unsigned char *) VIDEO;

	for (i = 0; i < COLUMNS * LINES * 2; i++)
		*(video + i) = 0;

	xpos = 0;
	ypos = 0;
}

/* Convert the integer D to a string and save the string in BUF. If
 BASE is equal to 'd', interpret that D is decimal, and if BASE is
 equal to 'x', interpret that D is hexadecimal. */
static void itoa(char *buf, int base, int d) {
	char *p = buf;
	char *p1, *p2;
	unsigned long ud = d;
	int divisor = 10;

	/* If %d is specified and D is minus, put `-' in the head. */
	if (base == 'd' && d < 0) {
		*p++ = '-';
		buf++;
		ud = -d;
	} else if (base == 'x')
		divisor = 16;

	/* Divide UD by DIVISOR until UD == 0. */
	do {
		int remainder = ud % divisor;

		*p++ = (remainder < 10) ? remainder + '0' : remainder + 'a' - 10;
	} while (ud /= divisor);

	/* Terminate BUF. */
	*p = 0;

	/* Reverse BUF. */
	p1 = buf;
	p2 = p - 1;
	while (p1 < p2) {
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2--;
	}
}

/* Put the character C on the screen. */
static void putchar(int c) {
	if (c == '\n' || c == '\r') {
		newline: xpos = 0;
		ypos++;
		if (ypos >= LINES
		)
			ypos = 0;
		return;
	}

	*(video + (xpos + ypos * COLUMNS) * 2) = c & 0xFF;
	*(video + (xpos + ypos * COLUMNS) * 2 + 1) = ATTRIBUTE;

	xpos++;
	if (xpos >= COLUMNS
	)
		goto newline;
}

static int ceil(double value)
{
	//printf("entrou no ceil");
	//printf("param = %d", (int)value);

	double aux = 0.0;
	aux += (int)value;

	if((value - aux) != 0.0)
	{
		//printf("entrou no if");
		return (int)value + 1;
	}
	return (int) value;
}

/* Format a string and print it on the screen, just like the libc
 function printf. */
void printf(const char *format, ...)
{
	char **arg = (char **) &format;
	int c;
	char buf[20];

	arg++;

	while ((c = *format++) != 0)
	{
		if (c != '%')
		putchar (c);
		else
		{
			char *p;

			c = *format++;
			switch (c)
			{
				case 'd':
				case 'u':
				case 'x':
				itoa (buf, c, *((int *) arg++));
				p = buf;
				goto string;
				break;

				case 's':
				p = *arg++;
				if (! p)
				p = "(null)";

				string:
				while (*p)
				putchar (*p++);
				break;

				default:
				putchar (*((int *) arg++));
				break;
			}
		}
	}
}

/* allocates "size" bytes and returns a pointer to the allocated memory */
void* malloc(size_t size)
{
	int allocIndex = 0;
	size_t found = 0;

	// calculate the necessary number of blocks
	size_t blocksNumber = /*(size_t)ceil(*/size/MEMORY_BLOCK_SIZE;//);

	// search a sequential number of free blocks
	for(int index = 0; index < BITMAP_LENGTH; index++)
	{
		// test if position is free(bit 0)
		if(bitmap[index] == 0)
		{
			// if is the first block found...
			if(found == 0)
			{
				// calculate allocation address to return
				allocIndex = index;
			}
			found++;
		}
		else
		{
			// not found, restart values
			allocIndex = 0;
			found = 0;
		}

		// if found the required number of blocks, return pointer to allocation address
		if(found == blocksNumber)
		{
			for (int i = allocIndex; i < (allocIndex + blocksNumber); i++)
			{
				bitmap[i] = 1;
			}
			return (void*)(allocIndex * 1024);
		}
	}

	return 0;
}

/* deallocates "size" bytes of memory */
void free(void* ptr, size_t size)
{
	// calculate the index of bitmap to free
	int index = /*ceil(*/(int)ptr/MEMORY_BLOCK_SIZE/*)*/;

	for(int i = index; i < index + (size/MEMORY_BLOCK_SIZE); i++)
	{
		bitmap[i] = 0;
	}
}

void printBitmap()
{
	printf("\n");

	for(int i = 0; i < BITMAP_LENGTH; i++)
	{
		printf(" Pointer %d ", i*1024);
	}

	printf("\n");

	for(int i = 0; i < BITMAP_LENGTH; i++)
	{
		printf("      %d       ", bitmap[i]);
	}

	printf("\n\n");
}

static __inline__ void disable()
{
   __asm__("cli");
}

static inline
unsigned char inb(unsigned short port)
{
    unsigned char ret;
    __asm__ volatile ("inb %1, %0"
        	: "=a"(ret)
		: "Nd"(port));
    return ret;
}

static inline
void outb(unsigned short port, unsigned char val)
{
    __asm__ volatile ("outb %0, %1"
        	:
		: "a"(val), "Nd"(port));
}

static __inline__ unsigned long long rdtsc(void)
{
   unsigned long long int x;
   __asm__ volatile (".byte 0x0f, 0x31"
		: "=A" (x));
   return x;
}


unsigned long speedCPU()
{

	unsigned char seg_sinal = 0;
	unsigned char seg_inicial = 0;
	unsigned char seg_final = 0;

//	printf("passou aki 1");

	unsigned long long ciclo_final = 0;
	unsigned long long ciclo_inicial = 0;
	unsigned long speed_cpu = 0;

//	printf("passou aki 2");

	disable();
	outb(RTCctrl, RTCsec);
	seg_sinal = inb(RTCdata);

//	printf("passou aki 3");
	while (seg_final == 0)
	{
		outb(RTCctrl, RTCsec);
		seg_sinal = inb(RTCdata);
		if ( seg_sinal != seg_inicial && seg_inicial == 0)
		{
			outb(RTCctrl, RTCsec);
			seg_inicial = inb(RTCdata);
			ciclo_inicial = rdtsc();
//			printf("passou aki 5");
		}
		else if (seg_sinal != seg_inicial )
		{
			ciclo_final = rdtsc();
			outb(RTCctrl, RTCsec);
			seg_final = inb(RTCdata);
//			printf("passou aki 6");
		}
	}
	printf("Ciclo inicial = %d\n", ciclo_inicial);

	printf("Ciclo final = %d\n", ciclo_final);

	speed_cpu = (ciclo_final - ciclo_inicial);
	speed_cpu = speed_cpu/1000000000;

	return speed_cpu;
}

void init_pics(int pic1, int pic2)
{
	/* send ICW1 */
	outb(PIC1, ICW1);
	outb(PIC2, ICW1);

	/* send ICW2 */
	outb(PIC1 + 1, pic1);	/* remap */
	outb(PIC2 + 1, pic2);	/*  pics */

	/* send ICW3 */
	outb(PIC1 + 1, 4);	/* IRQ2 -> connection to slave */
	outb(PIC2 + 1, 2);

	/* send ICW4 */
	outb(PIC1 + 1, ICW4);
	outb(PIC2 + 1, ICW4);

	/* disable all IRQs */
	outb(PIC1 + 1, 0xFF);
}




char* proc_nome()
{
    char *idcpu = "Id do fabricante do processador: 'xxxxxxxxxxxx'";

    __asm__ volatile(
    "movl %%eax, %%edi\n\t"
    "mov  $0, %%eax\n\t"
    "cpuid\n\t"
    "movl %%ebx, 34(%%edi)\n\t"
    "movl %%edx, 38(%%edi)\n\t"
    "movl %%ecx, 42(%%edi)\n\t"
    : /* output */
    : "a" (idcpu) /* input */
    : "ebx", "edx", "ecx", "edi"
    );

   return idcpu;
}




